package com.gpframework.common.pay;

import com.gpframework.common.constant.Constants;
import com.gpframework.common.utils.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;

//@Component
public class PayUtils implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(PayUtils.class);
    //请求域名
    @Value("${Pay.realmName}")
    public String getRealmName;
    public static String REALM_NAME;
    //appKey
    @Value("${Pay.appKey}")
    public String getAppKey;
    public static String appKey;
    //appSecrect
    @Value("${Pay.appSecrect}")
    public String getAppSecrect;
    public static String appSecrect;
    //appcodeValue (topup请求头参数)
    @Value("${Pay.appcodeValue}")
    public String getAppcodeValue;
    public static String APPCODE_VALUE;

    //原生端（android，iOS）用于获取对应充值产品的接口,根据货币和支付方式查询充值产品
    public static String GET_APP_RECHARGE_PRODUCT_URL;
    //Web端（官网）用于获取对应充值产品的接口,根据货币和支付方式查询充值产品
    public static String GET_WEB_RECHARGE_PRODUCT_URL;
    //Web端（官网）根据产品ID获取对应充值产品的信息
    public static String GET_WEB_PRODUCT_INFORMATION_URL;
    //Web端（官网）用于获取对应充值产品的接口
    public static String GET_WEB_PRODUCT_URL;
    //后台运营查询（用于查找第三方流水号）
    public static String ADMIN_QUERY_URL;
    //Apple端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String APPLE_PREPAY_URL;
    //Apple 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String APPLE_CALLBACK_URL;
    //Apple 辅助方法，用于处理回调丢失后，主动发起查询请求，并补尝走完整个交易流程
    public static String APPLE_RETRANSACTION_URL;

    //Google端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String GOOGLE_PREPAY_URL;
    //Google 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String GOOGLE_CALLBACK_URL;
    //Google 辅助方法，用于处理回调丢失后，主动发起查询请求，并补尝走完整个交易流程
    public static String GOOGLE_RETRANSACTION_URL;

    //MOLPay端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String MOLPAY_PREPAY_URL;
    //MOLPay 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String MOLPAY_CALLBACK_URL;

    //Paydibs端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String PAYDIBS_PREPAY_URL;
    //Paydibs 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String PAYDIBS_CALLBACK_URL;

    //Upay端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String UPAY_PREPAY_URL;
    //Upay 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String UPAY_CALLBACK_URL;

    //Paypal端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String PAYPAL_PREPAY_URL;

    //Paypal端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String QFPAY_PREPAY_URL;

    //Wechat端用来发起预支付动作，如果数据验证没问题，则返回此次的订单号(注：如果客户端提交过来充值商品被下架或者是金额发生变更，则返回业务码：3001)
    public static String WECHAT_PREPAY_URL;
    //Wechat 前端完成相关的支付动作后，需要提供相应的支付结果凭据.(注：如果客户端提交过来的订单并没有完成支付，则会返回业务码：3000 )复制接口复制文档复制地址
    public static String WECHAT_CALLBACK_URL;

    //增加支付渠道
    public static String ADDCHANNEL_URL;
    //增加新的充值产品
    public static String RECHARGEPACKAGE_ADD_URL;
    //编辑充值产品
    public static String RECHARGEPACKAGE_UPDATE_URL;
    //充值产品信息
    public static String GET_RECHARGEPACKAGE_INFO_URL;

    @Override
    public void afterPropertiesSet() throws Exception {
        PayUtils.REALM_NAME = this.getRealmName;
        PayUtils.appKey = this.getAppKey;
        PayUtils.appSecrect = this.getAppSecrect;
        PayUtils.APPCODE_VALUE = this.getAppcodeValue;
        PayUtils.GET_APP_RECHARGE_PRODUCT_URL = REALM_NAME + "/topup/client/native/query/";
        PayUtils.GET_WEB_RECHARGE_PRODUCT_URL = REALM_NAME + "/topup/query/";
        PayUtils.GET_WEB_PRODUCT_INFORMATION_URL = REALM_NAME + "/topup/item/load/";
        PayUtils.GET_WEB_PRODUCT_URL = REALM_NAME + "/topup/client/web/query/";
        PayUtils.ADMIN_QUERY_URL = REALM_NAME + "/admin/query";
        PayUtils.APPLE_PREPAY_URL = REALM_NAME + "/apple/prepay";
        PayUtils.APPLE_CALLBACK_URL = REALM_NAME + "/apple/callback";
        PayUtils.APPLE_RETRANSACTION_URL = REALM_NAME + "/apple/reTransaction";
        PayUtils.GOOGLE_PREPAY_URL = REALM_NAME + "/google/prepay";
        PayUtils.GOOGLE_CALLBACK_URL = REALM_NAME + "/google/notification";
        PayUtils.GOOGLE_RETRANSACTION_URL = REALM_NAME + "/google/reTransaction";
        PayUtils.MOLPAY_PREPAY_URL = REALM_NAME + "/molpay/submit";
        PayUtils.MOLPAY_CALLBACK_URL = REALM_NAME + "";
        PayUtils.PAYDIBS_PREPAY_URL = REALM_NAME + "/paydibs/submit";
        PayUtils.PAYDIBS_CALLBACK_URL = REALM_NAME + "";
//        PayUtils.PAYDIBS_CALLBACK_URL = REALM_NAME + "/paydibs/callback";
        PayUtils.UPAY_PREPAY_URL = REALM_NAME + "/upay/submit";
        PayUtils.UPAY_CALLBACK_URL = REALM_NAME + "/upay/callback";
        PayUtils.PAYPAL_PREPAY_URL = REALM_NAME + "/paypal/prepay";
        PayUtils.QFPAY_PREPAY_URL = REALM_NAME + "/qfpay/submit";
        PayUtils.WECHAT_PREPAY_URL = REALM_NAME + "/wechat/h5/submit";
        PayUtils.WECHAT_CALLBACK_URL = REALM_NAME + "/wechat/h5/callback";
        PayUtils.ADDCHANNEL_URL = REALM_NAME + "/topup/channel/add";
        PayUtils.RECHARGEPACKAGE_ADD_URL = REALM_NAME + "/topup/item/add";
        PayUtils.RECHARGEPACKAGE_UPDATE_URL = REALM_NAME + "/topup/item/update";
        PayUtils.GET_RECHARGEPACKAGE_INFO_URL = REALM_NAME + "/topup/item/load/";
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param, String contentType) {
        //使用UUID生成16位随机字符
        String uuid = UUID.randomUUID().toString();
        char[] cs = new char[16];
        for (int i = 0; i < 16; i++) {
            cs[i] = uuid.charAt(i);
        }
        String nonce = String.valueOf(cs);
        long time = System.currentTimeMillis();
        String sha1Hex = DigestUtils.sha1Hex(appSecrect + nonce + String.valueOf(time));
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            String urlNameString = url;
            log.info("sendPost - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setRequestProperty("App-Key", appKey);
            conn.setRequestProperty("Nonce", nonce);
            conn.setRequestProperty("Timestamp", String.valueOf(time));
            conn.setRequestProperty("Signature", sha1Hex);
            if (StringUtils.isNotBlank(contentType)) {
                conn.setRequestProperty("Content-Type", contentType);
            }
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        return sendGet(url, param, Constants.UTF8);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url         发送请求的 URL
     * @param param       请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType) {
        //使用UUID生成16位随机字符
        String uuid = UUID.randomUUID().toString();
        char[] cs = new char[16];
        for (int i = 0; i < 16; i++) {
            cs[i] = uuid.charAt(i);
        }
        String nonce = String.valueOf(cs);
        long time = System.currentTimeMillis();
        String sha1Hex = DigestUtils.sha1Hex(appSecrect + nonce + String.valueOf(time));
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.setRequestProperty("App-Key", appKey);
            connection.setRequestProperty("Nonce", nonce);
            connection.setRequestProperty("Timestamp", String.valueOf(time));
            connection.setRequestProperty("Signature", sha1Hex);
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPostDev(String url, String param, String contentType) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            String urlNameString = url;
            log.info("sendPost - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            if (StringUtils.isNotBlank(contentType)) {
                conn.setRequestProperty("Content-Type", contentType);
            }
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

}
